From: Timo Tijhof Date: Fri, 15 Jun 2018 20:20:14 +0000 (+0100) Subject: resourceloader: Embed 'mediawiki' directly in startup response X-Git-Tag: 1.34.0-rc.0~4935^2 X-Git-Url: http://git.cyclocoop.org/%22.%24match%5B1%5D.%22?a=commitdiff_plain;h=b7b84d55d4ea396167cba2fcf81369f48f1a1837;p=lhc%2Fweb%2Fwiklou.git resourceloader: Embed 'mediawiki' directly in startup response Embed the essential files to define mw.loader directly as part of the startup module. * This means the internal 'mediawiki' module no longer exists. This is safe to remove because: 1) While registered server-side for loading from startup.js, a PHPUnit structure test disallowed being specified as a dependency. 2) Anything that attempted to load it client-side failed because the module was marked in the registry as 'raw', thereby excluding it from the data sent to the client-side. As such, it was seen as an unknown module that the client refused to fetch from the server. * Deprecate getStartupModules() and getLegacyModules(). These are no longer needed. There are no known callers anywhere in Wikimedia Git or elsewhere indexed by Codesearch, but easy enough to leave as no-op for one release. * Remove ResourceLoaderRawFileModule class. No longer needed. Was created as a hack specifically for the 'mediawiki' module so that it would not leak global variables in debug mode. It has no usage anywhere in Wikimedia Git, nor elsewhere in Codesearch. Remove without deprecation given this was meant to be a 'private' class. * Introduce (private) getBaseModules(). Previously, this list only existed locally in getStartupModulesUrl() by merging getStartupModules() and getLegacyModules(). This value was factored out into its own method. * Make getStartupModulesUrl() private and rename to getBaseModulesUrl(). It is only used internally to export the 'baseModulesUri' value. Its name was already confusing before, but it would've been even more confusing now given it doesn't even call getStartupModules() any more. Bug: T192623 Change-Id: I14ba282d7b65e99ca54b7c2f77ba6e1adaddd11c --- diff --git a/RELEASE-NOTES-1.32 b/RELEASE-NOTES-1.32 index 3c60124d40..65bedfb9b5 100644 --- a/RELEASE-NOTES-1.32 +++ b/RELEASE-NOTES-1.32 @@ -219,6 +219,8 @@ because of Phabricator reports. context title is unset is now deprecated; anything creating an EditPage instance should set the context title via ::setContextTitle(). * The 'jquery.hidpi' module (polyfill for IMG srcset) is deprecated. +* ResourceLoaderStartUpModule::getStartupModules() and ::getLegacyModules() + are deprecated. These concepts are obsolete and have no replacement. === Other changes in 1.32 === * … diff --git a/autoload.php b/autoload.php index 6b1f981042..d3f7e980f4 100644 --- a/autoload.php +++ b/autoload.php @@ -1232,7 +1232,6 @@ $wgAutoloadLocalClasses = [ 'ResourceLoaderOOUIFileModule' => __DIR__ . '/includes/resourceloader/ResourceLoaderOOUIFileModule.php', 'ResourceLoaderOOUIImageModule' => __DIR__ . '/includes/resourceloader/ResourceLoaderOOUIImageModule.php', 'ResourceLoaderOOUIModule' => __DIR__ . '/includes/resourceloader/ResourceLoaderOOUIModule.php', - 'ResourceLoaderRawFileModule' => __DIR__ . '/includes/resourceloader/ResourceLoaderRawFileModule.php', 'ResourceLoaderSiteModule' => __DIR__ . '/includes/resourceloader/ResourceLoaderSiteModule.php', 'ResourceLoaderSiteStylesModule' => __DIR__ . '/includes/resourceloader/ResourceLoaderSiteStylesModule.php', 'ResourceLoaderSkinModule' => __DIR__ . '/includes/resourceloader/ResourceLoaderSkinModule.php', diff --git a/includes/resourceloader/ResourceLoaderRawFileModule.php b/includes/resourceloader/ResourceLoaderRawFileModule.php deleted file mode 100644 index beab53eb8d..0000000000 --- a/includes/resourceloader/ResourceLoaderRawFileModule.php +++ /dev/null @@ -1,52 +0,0 @@ -getBaseModulesUrl( $context ); return [ $url => [ 'as' => 'script' ] ]; } /** - * Base modules required for the base environment of ResourceLoader + * Internal modules used by ResourceLoader that cannot be depended on. * + * These module(s) should have isRaw() return true, and are not + * legal dependencies (enforced by structure/ResourcesTest). + * + * @deprecated since 1.32 No longer used. * @return array */ public static function getStartupModules() { - return [ 'jquery', 'mediawiki', 'mediawiki.base' ]; + wfDeprecated( __METHOD__, '1.32' ); + return []; } + /** + * @deprecated since 1.32 No longer used. + * @return array + */ public static function getLegacyModules() { + wfDeprecated( __METHOD__, '1.32' ); + return []; + } + + /** + * Base modules implicitly available to all modules. + * + * @since 1.32 + * @return array + */ + private function getBaseModules() { global $wgIncludeLegacyJavaScript; - $legacyModules = []; + $baseModules = [ 'jquery', 'mediawiki.base' ]; if ( $wgIncludeLegacyJavaScript ) { - $legacyModules[] = 'mediawiki.legacy.wikibits'; + $baseModules[] = 'mediawiki.legacy.wikibits'; } - return $legacyModules; + return $baseModules; } /** * Get the load URL of the startup modules. * - * This is a helper for getScript(), but can also be called standalone, such - * as when generating an AppCache manifest. + * This is a helper for getScript(). * * @param ResourceLoaderContext $context * @return string */ - public static function getStartupModulesUrl( ResourceLoaderContext $context ) { + private function getBaseModulesUrl( ResourceLoaderContext $context ) { $rl = $context->getResourceLoader(); $derivative = new DerivativeResourceLoaderContext( $context ); - $derivative->setModules( array_merge( - self::getStartupModules(), - self::getLegacyModules() - ) ); + $derivative->setModules( $this->getBaseModules() ); $derivative->setOnly( 'scripts' ); // Must setModules() before makeVersionQuery() $derivative->setVersion( $rl->makeVersionQuery( $derivative ) ); @@ -383,7 +399,15 @@ class ResourceLoaderStartUpModule extends ResourceLoaderModule { return '/* Requires only=script */'; } - $out = file_get_contents( "$IP/resources/src/startup.js" ); + $out = file_get_contents( "$IP/resources/src/startup/startup.js" ); + + // Keep in sync with maintenance/jsduck/eg-iframe.html and, + // keep in sync with 'fileHashes' in StartUpModule::getDefinitionSummary(). + $mwLoaderCode = file_get_contents( "$IP/resources/src/startup/mediawiki.js" ) . + file_get_contents( "$IP/resources/src/startup/mediawiki.requestIdleCallback.js" ); + if ( $context->getDebug() ) { + $mwLoaderCode .= file_get_contents( "$IP/resources/src/startup/mediawiki.log.js" ); + } $pairs = array_map( function ( $value ) { $value = FormatJson::encode( $value, ResourceLoader::inDebugMode(), FormatJson::ALL_OK ); @@ -394,13 +418,14 @@ class ResourceLoaderStartUpModule extends ResourceLoaderModule { '$VARS.wgLegacyJavaScriptGlobals' => $this->getConfig()->get( 'LegacyJavaScriptGlobals' ), '$VARS.configuration' => $this->getConfigSettings( $context ), // This url may be preloaded. See getPreloadLinks(). - '$VARS.baseModulesUri' => self::getStartupModulesUrl( $context ), + '$VARS.baseModulesUri' => $this->getBaseModulesUrl( $context ), ] ); $pairs['$CODE.registrations();'] = str_replace( "\n", "\n\t", trim( $this->getModuleRegistrations( $context ) ) ); + $pairs['$CODE.defineLoader();'] = $mwLoaderCode; return strtr( $out, $pairs ); } @@ -430,7 +455,9 @@ class ResourceLoaderStartUpModule extends ResourceLoaderModule { 'moduleHashes' => $this->getAllModuleHashes( $context ), 'fileHashes' => [ - $this->safeFileHash( "$IP/resources/src/startup.js" ), + $this->safeFileHash( "$IP/resources/src/startup/startup.js" ), + $this->safeFileHash( "$IP/resources/src/startup/mediawiki.js" ), + $this->safeFileHash( "$IP/resources/src/startup/mediawiki.requestIdleCallback.js" ), ], ]; return $summary; diff --git a/jsduck.json b/jsduck.json index e61303d345..28473aacb5 100644 --- a/jsduck.json +++ b/jsduck.json @@ -19,7 +19,7 @@ "resources/src/mediawiki.legacy", "resources/src/mediawiki.libs.jpegmeta/jpegmeta.js", "resources/src/mediawiki.skinning", - "resources/src/startup.js" + "resources/src/startup/startup.js" ], "--": [ "maintenance/jsduck/external.js", diff --git a/maintenance/jsduck/eg-iframe.html b/maintenance/jsduck/eg-iframe.html index 8475dcaf4e..2c7cd68823 100644 --- a/maintenance/jsduck/eg-iframe.html +++ b/maintenance/jsduck/eg-iframe.html @@ -41,8 +41,9 @@ mw.config = new mw.Map(); } + + - diff --git a/resources/Resources.php b/resources/Resources.php index b4b921fbfc..d0c52d846f 100644 --- a/resources/Resources.php +++ b/resources/Resources.php @@ -843,16 +843,6 @@ return [ /* MediaWiki */ - 'mediawiki' => [ - 'class' => ResourceLoaderRawFileModule::class, - // Keep in sync with maintenance/jsduck/eg-iframe.html - 'scripts' => [ - 'resources/src/mediawiki/mediawiki.js', - 'resources/src/mediawiki/mediawiki.requestIdleCallback.js', - ], - 'debugScripts' => 'resources/src/mediawiki/mediawiki.log.js', - 'targets' => [ 'desktop', 'mobile' ], - ], 'mediawiki.base' => [ // Keep in sync with maintenance/jsduck/eg-iframe.html 'scripts' => [ diff --git a/resources/src/mediawiki/mediawiki.js b/resources/src/mediawiki/mediawiki.js deleted file mode 100644 index 68d9d9e018..0000000000 --- a/resources/src/mediawiki/mediawiki.js +++ /dev/null @@ -1,2273 +0,0 @@ -/** - * Base library for MediaWiki. - * - * Exposed globally as `mediaWiki` with `mw` as shortcut. - * - * @class mw - * @alternateClassName mediaWiki - * @singleton - */ - -( function ( $ ) { - 'use strict'; - - var mw, StringSet, log, - hasOwn = Object.prototype.hasOwnProperty, - trackQueue = []; - - /** - * FNV132 hash function - * - * This function implements the 32-bit version of FNV-1. - * It is equivalent to hash( 'fnv132', ... ) in PHP, except - * its output is base 36 rather than hex. - * See - * - * @private - * @param {string} str String to hash - * @return {string} hash as an seven-character base 36 string - */ - function fnv132( str ) { - /* eslint-disable no-bitwise */ - var hash = 0x811C9DC5, - i; - - for ( i = 0; i < str.length; i++ ) { - hash += ( hash << 1 ) + ( hash << 4 ) + ( hash << 7 ) + ( hash << 8 ) + ( hash << 24 ); - hash ^= str.charCodeAt( i ); - } - - hash = ( hash >>> 0 ).toString( 36 ); - while ( hash.length < 7 ) { - hash = '0' + hash; - } - - return hash; - /* eslint-enable no-bitwise */ - } - - function defineFallbacks() { - // - StringSet = window.Set || ( function () { - /** - * @private - * @class - */ - function StringSet() { - this.set = {}; - } - StringSet.prototype.add = function ( value ) { - this.set[ value ] = true; - }; - StringSet.prototype.has = function ( value ) { - return hasOwn.call( this.set, value ); - }; - return StringSet; - }() ); - } - - /** - * Alias property to the global object. - * - * @private - * @static - * @member mw.Map - * @param {mw.Map} map - * @param {string} key - * @param {Mixed} value - */ - function setGlobalMapValue( map, key, value ) { - map.values[ key ] = value; - log.deprecate( - window, - key, - value, - // Deprecation notice for mw.config globals (T58550, T72470) - map === mw.config && 'Use mw.config instead.' - ); - } - - /** - * Log a message to window.console, if possible. - * - * Useful to force logging of some errors that are otherwise hard to detect (i.e., this logs - * also in production mode). Gets console references in each invocation instead of caching the - * reference, so that debugging tools loaded later are supported (e.g. Firebug Lite in IE). - * - * @private - * @param {string} topic Stream name passed by mw.track - * @param {Object} data Data passed by mw.track - * @param {Error} [data.exception] - * @param {string} data.source Error source - * @param {string} [data.module] Name of module which caused the error - */ - function logError( topic, data ) { - /* eslint-disable no-console */ - var msg, - e = data.exception, - source = data.source, - module = data.module, - console = window.console; - - if ( console && console.log ) { - msg = ( e ? 'Exception' : 'Error' ) + ' in ' + source; - if ( module ) { - msg += ' in module ' + module; - } - msg += ( e ? ':' : '.' ); - console.log( msg ); - - // If we have an exception object, log it to the warning channel to trigger - // proper stacktraces in browsers that support it. - if ( e && console.warn ) { - console.warn( e ); - } - } - /* eslint-enable no-console */ - } - - /** - * Create an object that can be read from or written to via methods that allow - * interaction both with single and multiple properties at once. - * - * @private - * @class mw.Map - * - * @constructor - * @param {boolean} [global=false] Whether to synchronise =values to the global - * window object (for backwards-compatibility with mw.config; T72470). Values are - * copied in one direction only. Changes to globals do not reflect in the map. - */ - function Map( global ) { - this.values = {}; - if ( global === true ) { - // Override #set to also set the global variable - this.set = function ( selection, value ) { - var s; - if ( arguments.length > 1 ) { - if ( typeof selection !== 'string' ) { - return false; - } - setGlobalMapValue( this, selection, value ); - return true; - } - if ( typeof selection === 'object' ) { - for ( s in selection ) { - setGlobalMapValue( this, s, selection[ s ] ); - } - return true; - } - return false; - }; - } - } - - Map.prototype = { - constructor: Map, - - /** - * Get the value of one or more keys. - * - * If called with no arguments, all values are returned. - * - * @param {string|Array} [selection] Key or array of keys to retrieve values for. - * @param {Mixed} [fallback=null] Value for keys that don't exist. - * @return {Mixed|Object|null} If selection was a string, returns the value, - * If selection was an array, returns an object of key/values. - * If no selection is passed, a new object with all key/values is returned. - */ - get: function ( selection, fallback ) { - var results, i; - fallback = arguments.length > 1 ? fallback : null; - - if ( Array.isArray( selection ) ) { - results = {}; - for ( i = 0; i < selection.length; i++ ) { - if ( typeof selection[ i ] === 'string' ) { - results[ selection[ i ] ] = hasOwn.call( this.values, selection[ i ] ) ? - this.values[ selection[ i ] ] : - fallback; - } - } - return results; - } - - if ( typeof selection === 'string' ) { - return hasOwn.call( this.values, selection ) ? - this.values[ selection ] : - fallback; - } - - if ( selection === undefined ) { - results = {}; - for ( i in this.values ) { - results[ i ] = this.values[ i ]; - } - return results; - } - - // Invalid selection key - return fallback; - }, - - /** - * Set one or more key/value pairs. - * - * @param {string|Object} selection Key to set value for, or object mapping keys to values - * @param {Mixed} [value] Value to set (optional, only in use when key is a string) - * @return {boolean} True on success, false on failure - */ - set: function ( selection, value ) { - var s; - // Use `arguments.length` because `undefined` is also a valid value. - if ( arguments.length > 1 ) { - if ( typeof selection !== 'string' ) { - return false; - } - this.values[ selection ] = value; - return true; - } - if ( typeof selection === 'object' ) { - for ( s in selection ) { - this.values[ s ] = selection[ s ]; - } - return true; - } - return false; - }, - - /** - * Check if one or more keys exist. - * - * @param {Mixed} selection Key or array of keys to check - * @return {boolean} True if the key(s) exist - */ - exists: function ( selection ) { - var i; - if ( Array.isArray( selection ) ) { - for ( i = 0; i < selection.length; i++ ) { - if ( typeof selection[ i ] !== 'string' || !hasOwn.call( this.values, selection[ i ] ) ) { - return false; - } - } - return true; - } - return typeof selection === 'string' && hasOwn.call( this.values, selection ); - } - }; - - defineFallbacks(); - - /* eslint-disable no-console */ - log = ( function () { - /** - * Write a verbose message to the browser's console in debug mode. - * - * This method is mainly intended for verbose logging. It is a no-op in production mode. - * In ResourceLoader debug mode, it will use the browser's console if available, with - * fallback to creating a console interface in the DOM and logging messages there. - * - * See {@link mw.log} for other logging methods. - * - * @member mw - * @param {...string} msg Messages to output to console. - */ - var log = function () {}, - console = window.console; - - // Note: Keep list of methods in sync with restoration in mediawiki.log.js - // when adding or removing mw.log methods below! - - /** - * Collection of methods to help log messages to the console. - * - * @class mw.log - * @singleton - */ - - /** - * Write a message to the browser console's warning channel. - * - * This method is a no-op in browsers that don't implement the Console API. - * - * @param {...string} msg Messages to output to console - */ - log.warn = console && console.warn && Function.prototype.bind ? - Function.prototype.bind.call( console.warn, console ) : - $.noop; - - /** - * Write a message to the browser console's error channel. - * - * Most browsers also print a stacktrace when calling this method if the - * argument is an Error object. - * - * This method is a no-op in browsers that don't implement the Console API. - * - * @since 1.26 - * @param {Error|...string} msg Messages to output to console - */ - log.error = console && console.error && Function.prototype.bind ? - Function.prototype.bind.call( console.error, console ) : - $.noop; - - /** - * Create a property on a host object that, when accessed, will produce - * a deprecation warning in the console. - * - * @param {Object} obj Host object of deprecated property - * @param {string} key Name of property to create in `obj` - * @param {Mixed} val The value this property should return when accessed - * @param {string} [msg] Optional text to include in the deprecation message - * @param {string} [logName=key] Optional custom name for the feature. - * This is used instead of `key` in the message and `mw.deprecate` tracking. - */ - log.deprecate = !Object.defineProperty ? function ( obj, key, val ) { - obj[ key ] = val; - } : function ( obj, key, val, msg, logName ) { - var logged = new StringSet(); - logName = logName || key; - msg = 'Use of "' + logName + '" is deprecated.' + ( msg ? ( ' ' + msg ) : '' ); - function uniqueTrace() { - var trace = new Error().stack; - if ( logged.has( trace ) ) { - return false; - } - logged.add( trace ); - return true; - } - // Support: Safari 5.0 - // Throws "not supported on DOM Objects" for Node or Element objects (incl. document) - // Safari 4.0 doesn't have this method, and it was fixed in Safari 5.1. - try { - Object.defineProperty( obj, key, { - configurable: true, - enumerable: true, - get: function () { - if ( uniqueTrace() ) { - mw.track( 'mw.deprecate', logName ); - mw.log.warn( msg ); - } - return val; - }, - set: function ( newVal ) { - if ( uniqueTrace() ) { - mw.track( 'mw.deprecate', logName ); - mw.log.warn( msg ); - } - val = newVal; - } - } ); - } catch ( err ) { - obj[ key ] = val; - } - }; - - return log; - }() ); - /* eslint-enable no-console */ - - /** - * @class mw - */ - mw = { - redefineFallbacksForTest: function () { - if ( !window.QUnit ) { - throw new Error( 'Reset not allowed outside unit tests' ); - } - defineFallbacks(); - }, - - /** - * Get the current time, measured in milliseconds since January 1, 1970 (UTC). - * - * On browsers that implement the Navigation Timing API, this function will produce floating-point - * values with microsecond precision that are guaranteed to be monotonic. On all other browsers, - * it will fall back to using `Date`. - * - * @return {number} Current time - */ - now: ( function () { - var perf = window.performance, - navStart = perf && perf.timing && perf.timing.navigationStart; - return navStart && typeof perf.now === 'function' ? - function () { return navStart + perf.now(); } : - function () { return Date.now(); }; - }() ), - - /** - * List of all analytic events emitted so far. - * - * @private - * @property {Array} - */ - trackQueue: trackQueue, - - track: function ( topic, data ) { - trackQueue.push( { topic: topic, timeStamp: mw.now(), data: data } ); - // The base module extends this method to fire events here - }, - - /** - * Track an early error event via mw.track and send it to the window console. - * - * @private - * @param {string} topic Topic name - * @param {Object} data Data describing the event, encoded as an object; see mw#logError - */ - trackError: function ( topic, data ) { - mw.track( topic, data ); - logError( topic, data ); - }, - - // Expose Map constructor - Map: Map, - - /** - * Map of configuration values. - * - * Check out [the complete list of configuration values](https://www.mediawiki.org/wiki/Manual:Interface/JavaScript#mw.config) - * on mediawiki.org. - * - * If `$wgLegacyJavaScriptGlobals` is true, this Map will add its values to the - * global `window` object. - * - * @property {mw.Map} config - */ - // Dummy placeholder later assigned in ResourceLoaderStartUpModule - config: null, - - /** - * Empty object for third-party libraries, for cases where you don't - * want to add a new global, or the global is bad and needs containment - * or wrapping. - * - * @property - */ - libs: {}, - - /** - * Access container for deprecated functionality that can be moved from - * from their legacy location and attached to this object (e.g. a global - * function that is deprecated and as stop-gap can be exposed through here). - * - * This was reserved for future use but never ended up being used. - * - * @deprecated since 1.22 Let deprecated identifiers keep their original name - * and use mw.log#deprecate to create an access container for tracking. - * @property - */ - legacy: {}, - - /** - * Store for messages. - * - * @property {mw.Map} - */ - messages: new Map(), - - /** - * Store for templates associated with a module. - * - * @property {mw.Map} - */ - templates: new Map(), - - // Expose mw.log - log: log, - - /** - * Client for ResourceLoader server end point. - * - * This client is in charge of maintaining the module registry and state - * machine, initiating network (batch) requests for loading modules, as - * well as dependency resolution and execution of source code. - * - * For more information, refer to - * - * - * @class mw.loader - * @singleton - */ - loader: ( function () { - - /** - * Fired via mw.track on various resource loading errors. - * - * @event resourceloader_exception - * @param {Error|Mixed} e The error that was thrown. Almost always an Error - * object, but in theory module code could manually throw something else, and that - * might also end up here. - * @param {string} [module] Name of the module which caused the error. Omitted if the - * error is not module-related or the module cannot be easily identified due to - * batched handling. - * @param {string} source Source of the error. Possible values: - * - * - style: stylesheet error (only affects old IE where a special style loading method - * is used) - * - load-callback: exception thrown by user callback - * - module-execute: exception thrown by module code - * - resolve: failed to sort dependencies for a module in mw.loader.load - * - store-eval: could not evaluate module code cached in localStorage - * - store-localstorage-init: localStorage or JSON parse error in mw.loader.store.init - * - store-localstorage-json: JSON conversion error in mw.loader.store.set - * - store-localstorage-update: localStorage or JSON conversion error in mw.loader.store.update - */ - - /** - * Fired via mw.track on resource loading error conditions. - * - * @event resourceloader_assert - * @param {string} source Source of the error. Possible values: - * - * - bug-T59567: failed to cache script due to an Opera function -> string conversion - * bug; see for details - */ - - /** - * Mapping of registered modules. - * - * See #implement and #execute for exact details on support for script, style and messages. - * - * Format: - * - * { - * 'moduleName': { - * // From mw.loader.register() - * 'version': '########' (hash) - * 'dependencies': ['required.foo', 'bar.also', ...] - * 'group': 'somegroup', (or) null - * 'source': 'local', (or) 'anotherwiki' - * 'skip': 'return !!window.Example', (or) null - * 'module': export Object - * - * // Set from execute() or mw.loader.state() - * 'state': 'registered', 'loaded', 'loading', 'ready', 'error', or 'missing' - * - * // Optionally added at run-time by mw.loader.implement() - * 'skipped': true - * 'script': closure, array of urls, or string - * 'style': { ... } (see #execute) - * 'messages': { 'key': 'value', ... } - * } - * } - * - * State machine: - * - * - `registered`: - * The module is known to the system but not yet required. - * Meta data is registered via mw.loader#register. Calls to that method are - * generated server-side by the startup module. - * - `loading`: - * The module was required through mw.loader (either directly or as dependency of - * another module). The client will fetch module contents from the server. - * The contents are then stashed in the registry via mw.loader#implement. - * - `loaded`: - * The module has been loaded from the server and stashed via mw.loader#implement. - * If the module has no more dependencies in-flight, the module will be executed - * immediately. Otherwise execution is deferred, controlled via #handlePending. - * - `executing`: - * The module is being executed. - * - `ready`: - * The module has been successfully executed. - * - `error`: - * The module (or one of its dependencies) produced an error during execution. - * - `missing`: - * The module was registered client-side and requested, but the server denied knowledge - * of the module's existence. - * - * @property - * @private - */ - var registry = {}, - // Mapping of sources, keyed by source-id, values are strings. - // - // Format: - // - // { - // 'sourceId': 'http://example.org/w/load.php' - // } - // - sources = {}, - - // For queueModuleScript() - handlingPendingRequests = false, - pendingRequests = [], - - // List of modules to be loaded - queue = [], - - /** - * List of callback jobs waiting for modules to be ready. - * - * Jobs are created by #enqueue() and run by #handlePending(). - * - * Typically when a job is created for a module, the job's dependencies contain - * both the required module and all its recursive dependencies. - * - * Format: - * - * { - * 'dependencies': [ module names ], - * 'ready': Function callback - * 'error': Function callback - * } - * - * @property {Object[]} jobs - * @private - */ - jobs = [], - - /** - * For #addEmbeddedCSS() and #addLink() - * - * @private - * @property {HTMLElement|null} marker - */ - marker = document.querySelector( 'meta[name="ResourceLoaderDynamicStyles"]' ), - - // For addEmbeddedCSS() - cssBuffer = '', - cssBufferTimer = null, - cssCallbacks = [], - rAF = window.requestAnimationFrame || setTimeout; - - /** - * Create a new style element and add it to the DOM. - * - * @private - * @param {string} text CSS text - * @param {Node|null} [nextNode] The element where the style tag - * should be inserted before - * @return {HTMLElement} Reference to the created style element - */ - function newStyleTag( text, nextNode ) { - var el = document.createElement( 'style' ); - el.appendChild( document.createTextNode( text ) ); - if ( nextNode && nextNode.parentNode ) { - nextNode.parentNode.insertBefore( el, nextNode ); - } else { - document.head.appendChild( el ); - } - return el; - } - - /** - * Add a bit of CSS text to the current browser page. - * - * The CSS will be appended to an existing ResourceLoader-created `